home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-08-31 | 28.0 KB | 669 lines | [TEXT/CCL ] |
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Copyright 1990 by Adam Chipkin for Apple Computer, Inc.
- ;;; Ruben Kleiman provided consultation and resource handling code.
- ;;;
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; ;;;
- ;;; Extension to Allegro Common Lisp ;;;
- ;;; to support calls to HyperTalk XMCDs and XFCNs ;;;
- ;;; ;;; ;;;
- ;;; Created: July 3, 1990 ;;;
- ;;; Last Mod: August 15, 1990 ;;;
- ;;; ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; -------------------------
- ;;; I N S T R U C T I O N S
- ;;; -------------------------
- ;;;
- ;;; (1) A. Copy the 'XCMD-Access' folder into the folder your 'Macintosh Allegro Lisp'
- ;;; application is in, then add its pathname to Allegro's '*module-search-path*'
- ;;; global variable:
- ;;;
- ;;; (pushnew (pathname "ccl;XCMD-Access:") *module-search-path*)
- ;;;
- ;;; Now Allegro will recognize 'xcmd-access.fasl' as a module and you can simply
- ;;; 'require' the module:
- ;;;
- ;;; (require 'xcmd-access)
- ;;;
- ;;; --OR--
- ;;;
- ;;; B. Just 'eval' this buffer.
- ;;;
- ;;;
- ;;; (2) Now the the two entry functions 'get-xcmd-handle' and 'do-xcmd' are interned.
- ;;; NOTE: You don't have to use 'get-xcmd-handle'; it just allows you to enhance
- ;;; the performance of 'do-xcmd'. When you give 'do-xcmd' the string name of the
- ;;; XCMD or XFCN resource, it calls 'get-xcmd-handle' for you. So if you plan to
- ;;; call a certain XCMD or XFCN repeatedly, you can avoid redundant calls to
- ;;; 'get-xcmd-handle' by getting and remembering the handle yourself once at the
- ;;; start, and then giving 'do-xcmd' the handle instead of the name each time you
- ;;; call it.
- ;;;
- ;;; Using 'do-xcmd' is straightforward: the first argument is the name of the XCMD
- ;;; or XFCN (or the handle to its resource) and all subsequent arguments are string
- ;;; parameters that are passed to the XCMD or XFCN. 'do-xmcd' returns the string
- ;;; returned by the XCMD or XFCN. See end of this file for examples...
- ;;;
- ;;;
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; ;;;
- ;;; D E F I N I T I O N S ;;;
- ;;; ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; Make sure traps are available:
- (require :traps)
-
- ;;; Establish pathname:
- (def-logical-pathname "xcmd" "ccl;XCMD-Access:")
-
- (eval-when (eval compile load)
- ;;; Define the XcmdBlock record type for communication to and from XCMDs:
- (defrecord (XCmdBlock :pointer)
- (paramCount integer)
- (param1 handle)
- (param2 handle)
- (param3 handle)
- (param4 handle)
- (param5 handle)
- (param6 handle)
- (param7 handle)
- (param8 handle)
- (param9 handle)
- (param10 handle)
- (param11 handle)
- (param12 handle)
- (param13 handle)
- (param14 handle)
- (param15 handle)
- (param16 handle)
- (returnValue handle)
- (passFlag boolean)
- (entryPoint pointer)
- (request integer)
- (result integer)
- (inarg1 pointer) ;; These were 'longint's, but they are sometimes used as pointers;
- (inarg2 pointer) ;; making the field-types 'pointer' stops Allegro from fiddling with
- (inarg3 pointer) ;; the high bits.
- (inarg4 pointer)
- (inarg5 pointer)
- (inarg6 pointer)
- (inarg7 pointer)
- (inarg8 pointer)
- (outarg1 pointer)
- (outarg2 pointer)
- (outarg3 pointer)
- (outarg4 pointer)))
-
- (eval-when (eval compile)
- ;; Define the callback request constants:
- (defconstant $xreqSendCardMessage 1)
- (defconstant $xreqEvalExpr 2)
- (defconstant $xreqStringLength 3)
- (defconstant $xreqStringMatch 4)
- (defconstant $xreqSendHCMessage 5)
- (defconstant $xreqZeroBytes 6)
- (defconstant $xreqPasToZero 7)
- (defconstant $xreqZeroToPas 8)
- (defconstant $xreqStrToLong 9)
- (defconstant $xreqStrToNum 10)
- (defconstant $xreqStrToBool 11)
- (defconstant $xreqStrToExt 12)
- (defconstant $xreqLongToStr 13)
- (defconstant $xreqNumToStr 14)
- (defconstant $xreqNumToHex 15)
- (defconstant $xreqBoolToStr 16)
- (defconstant $xreqExtToStr 17)
- (defconstant $xreqGetGlobal 18)
- (defconstant $xreqSetGlobal 19)
- (defconstant $xreqGetFieldByName 20)
- (defconstant $xreqGetFieldByNum 21)
- (defconstant $xreqGetFieldByID 22)
- (defconstant $xreqSetFieldByName 23)
- (defconstant $xreqSetFieldByNum 24)
- (defconstant $xreqSetFieldByID 25)
- (defconstant $xreqStringEqual 26)
- (defconstant $xreqReturnToPas 27)
- (defconstant $xreqScanToReturn 28)
- (defconstant $xreqScanToZero 39)
- (defconstant $xreqSendHCEvent 41)
-
- ;; Define the callback result constants:
- (defconstant $xresSucc 0)
- (defconstant $xresFail 1)
- (defconstant $xresNotImp 2)
-
- ;; Define the operating-system error constant 'noErr':
- (defconstant $noErr 0))
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; ;;;
- ;;; G L O B A L S ;;;
- ;;; ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (defvar *params*)
- (if (not (boundp '*params*))
- (setq *params* (make-record :XCmdBlock))) ; The XCmdBlock must be global to
- ; this code since the callback
- ; routine is not given its pointer
-
- (defvar *handles* nil) ; Handles needing eventual disposal (specifically,
- ; those created by the 'PasToZero' callback)
-
- (defvar *logstream* nil) ; A log of XCMD calls and callback activity is written
- ; to this stream
-
- (defvar *requests* ; Records the symbols of the existing callback requests
- (vector nil ; in a vector (index = requestNumber) for fast lookup
- 'x-SendCardMessage ; by the 'CALLBACK-HANDLER' function
- 'x-EvalExpr
- 'x-StringLength
- 'x-StringMatch
- 'x-SendHCMessage
- 'x-ZeroBytes
- 'x-PasToZero
- 'x-ZeroToPas
- 'x-StrToLong
- 'x-StrToNum
- 'x-StrToBool
- 'x-StrToExt
- 'x-LongToStr
- 'x-NumToStr
- 'x-NumToHex
- 'x-BoolToStr
- 'x-ExtToStr
- 'x-GetGlobal
- 'x-SetGlobal
- 'x-GetFieldByName
- 'x-GetFieldByNum
- 'x-GetFieldByID
- 'x-SetFieldByName
- 'x-SetFieldByNum
- 'x-SetFieldByID
- 'x-StringEqual
- 'x-ReturnToPas
- 'x-ScanToReturn
- nil
- nil
- nil
- nil
- nil
- nil
- nil
- nil
- nil
- nil
- 'x-ScanToZero
- nil
- 'x-SendHCEvent))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; ;;;
- ;;; S U P P O R T F U N C T I O N S ;;;
- ;;; ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; XCMD callback handler:
- ;;;
- ;;; The address of this function is passed, via the XcmdBlock, to the XCMD or XFCN to allow
- ;;; it to invoke our callback routines. The routine indicated by the 'request' field of the
- ;;; XcmdBlock is invoked if it's implemented; if it's not implemented, the function indicates
- ;;; that in the 'result' field of the XcmdBlock.
- ;;;
- ;;; IMPORTANT NOTE: HyperCard makes available to XCMDs all of the callback routines listed
- ;;; in above (in the definition of the '*REQUESTS*' variable). In this code, however, the
- ;;; majority are NOT implemented -- the callback handler simply ignores requests for
- ;;; unimplemented routines. I've provided the log feature so that you can see exactly
- ;;; which routines a given XCMD expects and only implement them as needed. The callbacks
- ;;; below should serve as models for any you'll need to write yourself. Note that all
- ;;; callback functions must be defined in the same package 'CALLBACK-HANDLER' is in.
- ;;;
- ;;; For reasonably thorough descriptions of the semantics of each callback routine, see
- ;;; pages 69-121 of Gary Bond's book 'XCMDs FOR HYPERCARD', MIS: Press, Portland, OR, 1988.
- ;;;
- (defpascal callback-handler ()
- (without-interrupts
- (let ((callback-fcn (aref *requests* (rref *params* :XCmdBlock.request)))
- callback-result)
-
- (if *logstream*
- (format *logstream* " [callback]: ~A (request code ~D)"
- (subseq (string callback-fcn) 2)
- (rref *params* :XCmdBlock.request)))
-
- (setq callback-fcn (fboundp callback-fcn))
- (setq callback-result
- (if callback-fcn
- (funcall callback-fcn) ;; If the function's here, call it and return its result.
- $xresNotImp)) ;; Else tell the XCMD that the callback is not implemented.
-
- (if *logstream*
- (format *logstream* "~%")))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Used for logging.
- ;;;
- ;;; Writes the given character string to the given stream; 'thestring' must be a pointer
- ;;; to a Pascal-type string (at most 255 characters, first byte is string's length).
- ;;;
- (defun write-pstring (thestream thestring)
- (let ((str-len (%get-byte thestring)))
- (do ((offset 1 (+ 1 offset))) ((> offset str-len))
- (write-char (code-char (%get-byte thestring offset)) thestream))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Used for logging.
- ;;;
- ;;; Writes the given character string to the given stream; 'thestring' must be a pointer
- ;;; to a C-type string (zero-terminated, no length byte).
- ;;;
- (defun write-cstring (thestream thestring)
- (do ((offset 0 (+ 1 offset))) ((= 0 (%get-byte thestring offset)))
- (write-char (code-char (%get-byte thestring offset)) thestream)))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Used by the 'equal-pstr-cstr' function.
- ;;;
- ;;; CHAR-BYTES= returns true if the ASCII characters represented by the two given integers
- ;;; are equal and CHAR-BYTES/= returns false if they're equal (both are case insensitive)
- ;;;
- (defmacro char-bytes= (b1 b2)
- `(char-equal (int-char (coerce ,b1 'integer)) (int-char (coerce ,b2 'integer))))
- (defmacro char-bytes/= (b1 b2)
- `(char-not-equal (int-char (coerce ,b1 'integer)) (int-char (coerce ,b2 'integer))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Used by the 'StringMatch' callback function.
- ;;;
- ;;; Returns true if the two strings match (case insensitive). 'pstr' must be a pointer to a
- ;;; Pascal-type string and 'cstr' must be a pointer to a C-type string.
- ;;;
- (defun equal-pstr-cstr (pstr cstr)
- (let ((pstr-len (%get-byte pstr)))
- (incf pstr)
- (do ((offset 0 (+ 1 offset)))
- ((or (= offset pstr-len)
- (= 0 (%get-byte cstr))
- (char-bytes/= (%get-byte pstr) (%get-byte cstr)))
- (if (= offset pstr-len)
- t
- (char-bytes= (%get-byte pstr) (%get-byte cstr))))
- (incf pstr)
- (incf cstr))))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; The 'StringMatch' callback function:
- ;;;
- ;;; In the XcmdBlock, inArg1 points to a Pascal-type string, the pattern, and inArg2 points
- ;;; to a C-type string, the target.
- ;;;
- ;;; Performs a case-insensitive search to locate the pattern within the target. If the pattern
- ;;; string is found in the target, a pointer to the first character of the match is returned
- ;;; in outArg1, otherwise the null-pointer is returned in outArg1.
- ;;;
- (defun x-StringMatch ()
- (let ((pattern (%ptr-to-int (rref *params* :XCmdBlock.inArg1))) ; str255 to locate
- (target (%ptr-to-int (rref *params* :XCmdBlock.inArg2)))) ; c-string in which to look
- (rset *params* :XCmdBlock.outArg1 (%int-to-ptr #x00000000)) ; Default to NULL.
- (when (and (/= 0 pattern) (/= 0 target))
- (do () ((or (= 0 (%get-byte target)) (equal-pstr-cstr pattern target)))
- (incf target))
- (if (/= 0 (%get-byte target))
- (rset *params* :XCmdBlock.outArg1 (%int-to-ptr target))))
-
- (when *logstream*
- (format *logstream* " Substring '")
- (write-pstring *logstream* pattern)
- (if (= 0 (%ptr-to-int (rref *params* :XCmdBlock.outArg1)))
- (format *logstream* "' not in '")
- (format *logstream* "' in '"))
- (write-cstring *logstream* target)
- (format *logstream* "'."))
-
- $xresSucc))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; The 'ZeroToPas' callback function:
- ;;;
- ;;; In the XcmdBlock, inArg1 points to a C-type string, the source, and inArg2 points
- ;;; to a Pascal-type string, the destination.
- ;;;
- ;;; Copies the C-string into the Pascal-string's buffer in the Pascal-string format.
- ;;;
- (defun x-ZeroToPas ()
- (let ((cstr (%ptr-to-int (rref *params* :XCmdBlock.inArg1))) ;; c-string
- (pstr (%ptr-to-int (rref *params* :XCmdBlock.inArg2))) ;; str255
- (str-len 0))
- (do () ((= 0 (%get-byte cstr)))
- (incf str-len)
- (%put-byte pstr (%get-byte cstr) str-len)
- (incf cstr))
- (%put-byte pstr str-len)
-
- (when *logstream*
- (format *logstream* " STRING = '")
- (write-pstring *logstream* pstr)
- (format *logstream* "'"))
-
- $xresSucc))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; The 'PasToZero' callback function:
- ;;;
- ;;; In the XcmdBlock, inArg1 points to a Pascal-type string.
- ;;;
- ;;; Allocates a handle to enough memory to hold the given string then copies the string there,
- ;;; terminated by a zero byte. The C-string handle is returned in outArg1.
- ;;; NOTE: The XCMD is not expected to dispose of the C-string created by this callback, so
- ;;; the handle is remembered in the global list *handles* and is disposed of by the 'do-xcmd'
- ;;; function when the XCMD finishes executing.
- ;;;
- (defun x-PasToZero ()
- (let* ((pstr (%ptr-to-int (rref *params* :XCmdBlock.inArg1))) ;; str255
- (len (%get-byte pstr))
- (h (_NewHandle :D0 (%int-to-ptr (+ 1 len)) :A0)))
- (setq *handles* (cons h *handles*))
- (rset *params* :XCmdBlock.outArg1 h)
- (incf pstr)
- (with-dereferenced-handles ((p h))
- (%put-byte p 0 len)
- (do ((offset 0 (+ 1 offset))) ((= offset len))
- (%put-byte p (%get-byte pstr offset) offset))
-
- (when *logstream*
- (format *logstream* " STRING = '")
- (write-cstring *logstream* (%ptr-to-int p))
- (format *logstream* "'")))
-
- $xresSucc))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; The 'ScanToZero' callback function:
- ;;;
- ;;; In the XcmdBlock, inArg1 is a handle to a C-type string.
- ;;;
- ;;; Updates the handle's master pointer to point to the next zero byte in the string.
- ;;;
- (defun x-scanToZero ()
- (let ((handle (rref *params* :XCmdBlock.inArg1)) ; handle to c-string
- (offset 0))
- (with-dereferenced-handles ((pointer handle))
- (loop
- (when (eq (%get-byte pointer offset) 0)
- (%put-ptr handle (%int-to-ptr (+ (%ptr-to-int pointer) offset)))
- (return nil))
- (incf offset))
-
- (when *logstream*
- (format *logstream* " STRING = '")
- (write-cstring *logstream* (%ptr-to-int pointer))
- (format *logstream* "'")))
-
- $xresSucc))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; The 'StringToNum' callback function:
- ;;;
- ;;; In the XcmdBlock, inArg1 points to a Pascal-type string.
- ;;;
- ;;; Returns a signed long-integer in outArg1 equal to the number represented by the string.
- ;;;
- (defun x-StringToNum ()
- (let* ((str (rref *params* :XCmdBlock.inArg1)) ;; str255
- (num (parse-integer (%get-string str))))
-
- (if *logstream*
- (format *logstream* " NUMBER = ~D" num))
-
- (rset *params* :XCmdBlock.outArg1 num)))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Used by the 'do-xcmd' function.
- ;;;
- ;;; Given a lisp string, allocates a handle to enough memory to hold the string
- ;;; then copies the string there, terminated by a zero byte
- ;;;
- (defun make-cstr-handle (lisp-string)
- (let* ((len (length lisp-string))
- (cstr-handle (_NewHandle :D0 (%int-to-ptr (+ 1 len)) :A0)))
- (with-dereferenced-handles ((cstr-ptr cstr-handle))
- (%put-byte cstr-ptr 0 len)
- (do ((offset 0 (+ 1 offset))) ((= offset len))
- (%put-byte cstr-ptr (aref lisp-string offset) offset)))
- cstr-handle))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; ;;;
- ;;; E N T R Y F U N C T I O N S ;;;
- ;;; ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Get-XCMD-Handle:
- ;;;
- ;;; Returns a handle to the named 'XCMD' or 'XFCN' resource. If that resource
- ;;; is not in the currently open resource file, the function tries to find it
- ;;; in the file given by 'resource-file-path'. If 'resource-file-path' is nil,
- ;;; the function tries to find it in a file named <xcmd-name>, <xcmd-name> + '.xcmd',
- ;;; or <xcmd-name> + '.xfcn' in the 'ccl;' folder or the 'xcmd;' folder.
- ;;;
- ;;; By default, the function first tries to get an XCMD with the given name
- ;;; and, upon failure, tries to get an XFCN with that name. To force it to
- ;;; try in the reverse order, supply T for the optional 'XFCN-P' argument.
- ;;;
- (defun get-xcmd-handle (xcmd-name &optional resource-file-path xfcn-p)
- (with-pstrs ((xcmd-name-pstr xcmd-name))
- (let* ((old-currentResFile (_CurResFile :errchk :word))
- (xcmd-handle (if xfcn-p
- (_GetNamedResource :ostype "XFCN" :ptr xcmd-name-pstr :ptr)
- (_GetNamedResource :ostype "XCMD" :ptr xcmd-name-pstr :ptr)))
- (err (_ResError :word)))
- (when (/= err $noErr)
- (setq xcmd-handle (if xfcn-p
- (_GetNamedResource :ostype "XCMD" :ptr xcmd-name-pstr :ptr)
- (_GetNamedResource :ostype "XFCN" :ptr xcmd-name-pstr :ptr)))
- (setq err (_ResError :word)))
- (when (/= err $noErr)
- (when (null resource-file-path)
- (setq resource-file-path
- (or (probe-file (format nil "ccl;~A" xcmd-name))
- (probe-file (format nil "ccl;~A.xcmd" xcmd-name))
- (probe-file (format nil "ccl;~A.xfcn" xcmd-name))
- (probe-file (format nil "xcmd;~A" xcmd-name))
- (probe-file (format nil "xcmd;~A.xcmd" xcmd-name))
- (probe-file (format nil "xcmd;~A.xfcn" xcmd-name)))))
- (cond
- ((null resource-file-path)
- (error "Can't find XCMD/XFCN resource '~A'" xcmd-name))
- ((not (probe-file resource-file-path))
- (error "Can't find resource file '~A'"
- (expand-logical-namestring resource-file-path)))
- ((= -1 (with-pstrs ((path-str (expand-logical-namestring resource-file-path)))
- (_OpenResFile :errchk :ptr path-str :word)))
- (_UseResFile :errchk :word old-currentResFile)
- (error "Bad resource file '~A'" (expand-logical-namestring resource-file-path)))
- (t
- (setq xcmd-handle (_GetNamedResource :ostype "XCMD" :ptr xcmd-name-pstr :ptr))
- (setq err (_ResError :word))
- (when (/= err $noErr)
- (setq xcmd-handle (_GetNamedResource :ostype "XFCN" :ptr xcmd-name-pstr :ptr))
- (setq err (_ResError :word)))
- (when (/= err $noErr)
- (error "Can't find XCMD/XFCN resource '~A' in file '~A'" xcmd-name
- (expand-logical-namestring resource-file-path))))))
- (when (= 0 (%ptr-to-int xcmd-handle))
- (error "Bad XCMD/XFCN resource '~A'" xcmd-name))
-
- (_UseResFile :errchk :word old-currentResFile)
- (_HNoPurge :A0 xcmd-handle)
- xcmd-handle)))
-
-
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; Do-XCMD:
- ;;;
- ;;; Given the string name or the handle of an XCMD or XFCN resource and up
- ;;; to 16 string parameters, this invokes the XCMD/XFCN, passing it the given
- ;;; parameters. It returns the string result of the call (for an XCMD, the
- ;;; error string; for an XFCN, the function value).
- ;;;
- (defun do-xcmd (xcmd-handle-or-name &rest paramList)
- (unless (or (handlep xcmd-handle-or-name)
- (and (stringp xcmd-handle-or-name)
- (setq xcmd-handle-or-name (get-xcmd-handle xcmd-handle-or-name))))
- (error "Invalid XCMD/XFCN"))
-
- (if *logstream*
- (format *logstream* "~%---> Now calling XCMD/XFCN at ~D...~%~%" xcmd-handle-or-name))
-
- (let ((old-resource-state (_HGetState :A0 (%int-to-ptr xcmd-handle-or-name) :D0)))
- (_MoveHHi :A0 (%int-to-ptr xcmd-handle-or-name))
- (_HLock :A0 (%int-to-ptr xcmd-handle-or-name))
-
- (unwind-protect
- (progn
- ;; --- SET UP XCMD-BLOCK ---
- (rset *params* :XCmdBlock.paramCount (list-length paramList))
- (do ((plist paramList (cdr plist))
- (param (+ 2 (%ptr-to-int *params*)) (+ param 4))
- (numparams 1 (1+ numparams)))
- ((> numparams 16))
- (if (null plist)
- (%put-ptr param (%int-to-ptr 0))
- (progn
- (unless (stringp (car plist))
- (error "~:r XCMD parameter (~:r function argument) is not a string"
- numparams (1+ numparams)))
- (%put-ptr param (make-cstr-handle (car plist)))
- (setq *handles* (cons (%get-ptr param) *handles*)))))
- (rset *params* :XCmdBlock.returnValue (%int-to-ptr 0))
- (rset *params* :XCmdBlock.entryPoint (%int-to-ptr callback-handler))
-
- ;; --- JUMP TO THE XCMD/XFCN RESOURCE ---
- (with-dereferenced-handles ((xcmd-ptr xcmd-handle-or-name))
- (ccl::ff-call xcmd-ptr :ptr *params* :novalue)))
-
- ;; --- RESTORE THE RESOURCE'S ORIGINAL STATE ---
- (_HSetState :A0 (%int-to-ptr xcmd-handle-or-name) :D0 old-resource-state)
-
- ;; --- CONVERT THE XCMD/XFCN's RESULT C-STRING TO A LISP STRING ---
- (let ((cstr-handle (%ptr-to-int (rref *params* :XCmdBlock.returnValue)))
- (lisp-result-string ""))
- (when (/= 0 cstr-handle)
- (let ((pstr (_NewPtr :D0 (%int-to-ptr 256) :A0)) ;; str255
- (str-len 0))
- (if (/= 0 (%ptr-to-int cstr-handle))
- (with-dereferenced-handles ((cstr cstr-handle))
- (setq cstr (%ptr-to-int cstr))
- (do () ((= 0 (%get-byte cstr)))
- (incf str-len)
- (%put-byte pstr (%get-byte cstr) str-len)
- (incf cstr))))
- (%put-byte pstr str-len)
- (setq lisp-result-string (%get-string pstr))
- (_DisposPtr :A0 (%int-to-ptr pstr))))
-
- ;; --- DISPOSE OF MEMORY ALLOCATED BY 'PasToZero' CALLBACKS (AND PARAMETER STRINGS) ---
- (do () ((null *handles*))
- (_DisposHandle :A0 (car *handles*))
- (setq *handles* (cdr *handles*)))
-
- (if *logstream*
- (format *logstream* "~%The XCMD has finished executing.~%~A~%" lisp-result-string))
-
- ;; --- RETURN "THE RESULT" OF THE XCMD CALL (OR THE FUNCTION VALUE OF THE XFCN CALL) ---
- lisp-result-string))))
-
-
- (provide 'xcmd-access)
- (pushnew :xcmd-access *features*)
-
-
-
- #|
-
- EXAMPLES:
- --------
-
- (setq *logstream* *standard-output*)
-
-
- To use MacroMind's 'PlayMovie' XCMD --
-
- 1. With ResEdit, create a resource file containing the XCMD and place
- it into the folder your 'Macintosh Allegro Lisp' application is in.
-
- 2. Copy the 'MacroMind Player' application, as well as the movies you
- want to play and the sounds they use, into the folder your 'Lisp'
- application is in.
-
- 3. Go!
-
-
- ;;; Without preloading the XCMD or the movie:
- (do-xcmd "PlayMovie" "Explosion" "movieNoClear" "movieNoUpdate")
-
-
- ;;; Preloading the XCMD but not the movie:
- (setq playMovie (get-xcmd-handle "PlayMovie"))
- (do-xcmd playMovie "Explosion" "movieNoClear" "movieNoUpdate")
-
-
- ;;; Preloading the movie but not the XCMD:
- (do-xcmd "PlayMovie" "Explosion" "moviePreload")
- (do-xcmd "PlayMovie" "movieNoClear" "movieNoUpdate")
-
-
- ;;; Preloading both the XCMD and the movie:
- (setq playMovie (get-xcmd-handle "PlayMovie"))
- (do-xcmd playMovie "Explosion" "moviePreload")
- (do-xcmd playMovie "movieNoClear" "movieNoUpdate")
-
-
- |#
-